package edu.vt.ece.rt.benchmark.counter;

import java.util.ArrayList;
import java.util.Random;

import edu.vt.ece.rt.benchmark.BenchmarkThread;
import edu.vt.ece.rt.dstm.Context;
import edu.vt.ece.rt.dstm.ContextDelegator;
import edu.vt.ece.rt.dstm.QObject;
import edu.vt.ece.rt.dstm.TMProxy;
import edu.vt.ece.rt.quorum.ClustersManager;
import edu.vt.ece.rt.quorum.Resource;
import edu.vt.ece.rt.quorum.util.AbortException;
import edu.vt.ece.rt.quorum.util.Names;
import edu.vt.ece.rt.util.Logger.Logger;

public class Counter extends QObject {


	Integer count;
	private String id;

	public Counter(String ID, int count) {
		id = ID;
		this.count = new Integer(count);
		Resource.putQObject(getId(), this);
	}

	public Counter () {

	}

	@Override
	public QObject clone() {
		Counter c = new Counter();
		c.id = getId();
		c.setVersion(getVersion());
		c.setCloneOwner(getCloneOwner());
		c.count = new Integer(count);
		return c;
	}

	@Override
	public String getId() {
		return id;
	}

	public static Integer read(Context parentContext) {

		Integer count = 0;
		Long priority = System.currentTimeMillis();

		for (int i = 0; i < 0x7fffffff; i++) {

			((BenchmarkThread)Thread.currentThread()).collectStatsRoot(i);
			Context context = ContextDelegator.getNestedContext();
			context.setParent(parentContext);
			context.setDescription("read element of counter");
			context.sanityNested();
			context.setPriority(priority);

			Logger.debug("----Started----- R Transaction "+context.getContextId()+" on "+ClustersManager.baseClusterId);
			try {

				Counter c = (Counter) TMProxy.getTMProxy().read("counter", context.getContextId());

				count = c.count;

				if (TMProxy.getTMProxy().requestCommit(context.getContextId())) {
					context.unsetParent(parentContext);
					return count;
				}
			} catch (AbortException e) {
				Logger.debug("Aborting in loop "+context.getContextId());
				e.touch();
			}
		}
		Logger.fetal("Unable to complete transaction in given number of tries");

		return count;
	}



	public static void write(boolean nesting) {

		Context parentContext = null;
		Long priority = System.currentTimeMillis();

		for (int i = 0; i < 0x7fffffff; i++) {

			((BenchmarkThread)Thread.currentThread()).collectStatsRoot(i);
			Context context = ContextDelegator.getNestedContext();
			context.setParent(parentContext);
			context.setDescription("starting root transaction");
			context.sanityNested();
			context.setPriority(priority);


			Logger.debug("----Started----- R Transaction "+context.getContextId()+" on "+ClustersManager.baseClusterId);
			try {

				for (int j = 0; j < Integer.getInteger(Names.calls);j++) {


					if (nesting)
						updateCounterNested(context);
					else
						updateCounter(context, i);


				}

				if (TMProxy.getTMProxy().requestCommit(context.getContextId())) {
					context.unsetParent(parentContext);
					return;
				}

			}  catch (AbortException e) {
				e.touch();
			}
		}

	}


	private static void updateCounter(Context context, int i ) throws AbortException {

		Random r = new Random();
		try {
			Thread.sleep(r.nextInt(i+1) * Integer.getInteger(Names.nodes).intValue());
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		Counter c = (Counter) TMProxy.getTMProxy().write("counter", context.getContextId());


		if (c.count == null) {
			System.out.println("null val");
		}
		c.count = new Integer(c.count+1);


	}


	public static void updateCounterNested(Context parentContext) throws AbortException {



		Long priority = System.currentTimeMillis();

		for (int i = 0; i < 0x7fffffff; i++) {

			((BenchmarkThread)Thread.currentThread()).collectStatsInner(i);
			Context context = ContextDelegator.getNestedContext();
			context.setParent(parentContext);
			context.setDescription("insert element in skiplist");
			context.sanityNested();
			context.setPriority(priority);

			try {
				updateCounter(context, i);
				context.mergeParent();
				return;
			} catch (AbortException e) {
				if (e.getTransaction().longValue() != context.getContextId().longValue()) {
					Logger.debug("Exception in parent transaction "+e.getTransaction());
					//					System.out.println("Exception in parent transaction "+e.getTransaction());
					throw e;
				} else {
					Logger.debug("Exception in current transaction "+e.getTransaction());
					//					System.out.println("Exception in current transaction "+e.getTransaction());					
				}
			}
		}
		Logger.fetal("Unable to complete transaction in given number of tries");

	}


}
